﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using BiljartHandicap.AuthenticationWebServiceReference;
using BiljartHandicap.Navigation;
using BiljartHandicap.UserValidationWebServiceReference;
using BiljartHandicap.Util;

namespace BiljartHandicap.ViewModel
{
    public class LoginViewModel : ViewModel, INotifyDataErrorInfo, INavigable
    {
        #region Variables + Constants
        private string _userName;
        private string _password;
        private string _status;

        private readonly Dictionary<string, ObservableCollection<string>> _validationErrors;
        private readonly ObservableCollection<string> _classValidationErrors;
        #endregion

        public LoginViewModel()
        {
            _validationErrors = new Dictionary<string, ObservableCollection<string>>();
            _classValidationErrors = new ObservableCollection<string>();

            //Create error collection for all the public fields
            CreateErrorsCollection("UserName");
            CreateErrorsCollection("Password");
            CreateErrorsCollection("Status");
        }

        public string UserName
        {
            get { return _userName; }
            set
            {
                _userName = value;
                ValidateUserName(value);
                NotifyPropertyChanged("UserName");
            }
        }

        public string Password
        {
            get { return _password; }
            set
            {
                _password = value;
                NotifyPropertyChanged("Password");
            }
        }

        //TODO make this field unnecessary ( wa een woord :) )
        public string Status
        {
            get { return _status; }
            set
            {
                _status = value;
                NotifyPropertyChanged("Status");
            }
        }

        #region LoginCommand

        private ViewModelCommand _logInCommand;

        public ViewModelCommand LogInCommand
        {
            get
            {
                if (_logInCommand == null)
                {
                    _logInCommand = new ViewModelCommand(
                        p => LogIn(),
                        p => CanLogIn
                        );
                }
                return _logInCommand;
            }
        }

        /// <summary>
        /// Called by LogInCommand
        /// Tries to login to user
        /// </summary>
        private void LogIn()
        {
            var client = new AuthenticationServiceClient();
            client.LoginCompleted += (sender, ea) =>
                                         {
                                             if (ea.Result)
                                             {
                                                 // If the result is true the user is logged in and is navigated to the Dashboardview
                                                 NavigationService.Navigate("/Views/DashboardView.xaml");
                                             }
                                             else
                                             {
                                                 Status = "You tried to hack this account. The FBI has been alerted. RUN!!!";
                                             }
                                         };
            client.LoginAsync(UserName, Password, null, false);
        }

        /// <summary>
        /// Checks if there are errors on the fields Username and password
        /// </summary>
        private bool CanLogIn
        {
            get {
                return _validationErrors["UserName"].Count == 0 && _validationErrors["Password"].Count == 0;
            }
        }

        #endregion

        #region CheckForUpdateCommand

        private ViewModelCommand _checkForUpdateCommand;

        public ViewModelCommand CheckForUpdateCommand
        {
            get
            {
                return new ViewModelCommand(
                    p => CheckForUpdate(),
                    p => true //TODO: add update logic (can a program update?)
                    );
            }
        }

        /// <summary>
        /// Called by CheckForUpdateCommand
        /// Tries to update the application
        /// </summary>
        private static void CheckForUpdate()
        {
            Application.Current.CheckAndDownloadUpdateCompleted += (sender, ea) =>
                                                                       {
                                                                           if (ea.UpdateAvailable)
                                                                           {
                                                                               var notificationWindow =
                                                                                   new NotificationWindow();
                                                                               notificationWindow.Content =
                                                                                   new TextBlock()
                                                                                       {Text = "Program updated"};
                                                                               notificationWindow.Show(5000);
                                                                           }
                                                                       };
            Application.Current.CheckAndDownloadUpdateAsync();
        }

        #endregion

        #region Members of INotifyDataErrorInfo

        public IEnumerable GetErrors(string propertyName)
        {
            if (!string.IsNullOrEmpty(propertyName))
            {
                if (_validationErrors.ContainsKey(propertyName))
                    return _validationErrors[propertyName];
                else
                    return null;
            }
            else
            {
                return _classValidationErrors;
            }
        }

        public bool HasErrors
        {
            get
            {
                if (_classValidationErrors.Count > 0)
                    return true;
                foreach (string key in _validationErrors.Keys)
                {
                    if (_validationErrors[key].Count > 0)
                        return true;
                }
                return false;
            }
        }

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        /// <summary>
        /// Creates an error collection for a field by adding an observable collection to the validation errors
        /// </summary>
        /// <param name="propertyName">Property name to make a collection for</param>
        private void CreateErrorsCollection(string propertyName)
        {
            if (!_validationErrors.ContainsKey(propertyName))
            {
                _validationErrors.Add(propertyName, new ObservableCollection<string>());
            }
        }

        private void ValidateUserName(string username)
        {
            var client = new UserValidationWebServiceClient();
            client.ValidateUsernameCompleted += (o, ea) =>
                                                    {
                                                        var result = ea.Result;
                                                        _validationErrors["UserName"].Clear();
                                                        _validationErrors["UserName"] = result["UserName"];
                                                        if (ErrorsChanged != null)
                                                        {
                                                            ErrorsChanged(this, new DataErrorsChangedEventArgs("UserName"));
                                                        }
                                                    };
            client.ValidateUsernameAsync(username);

        }

        #endregion

        #region Members of INavigable
        
        public INavigationService NavigationService
        {
            get;
            set;
        }
        
        #endregion
    }
}
